package com.zhuangjie.security.helper;

import com.zhuangjie.security.entity.AuthenticationTokens;
import com.zhuangjie.security.helper.config.JwtHelperConfig;
import com.zhuangjie.security.helper.config.td.AllTokenDataKeys;
import com.zhuangjie.security.utils.HeaderMapRequestWrapper;
import org.mockito.Mockito;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.util.HashMap;
import java.util.Map;
import java.util.function.BiConsumer;

/**
 * 模块安全帮助程序
 *
 * @author zhuangjie
 * @date 2024/04/02
 */
@Component
public class ModuleSecurityHelper {
    private static JwtHelperConfig jwtHelperConfig;

    private static JwtHelper jwtHelper;

    public ModuleSecurityHelper(JwtHelperConfig jwtHelperConfig, JwtHelper jwtHelper) throws Exception {
        ModuleSecurityHelper.jwtHelper = jwtHelper;
        ModuleSecurityHelper.jwtHelperConfig = jwtHelperConfig;
    }



    /**
     * 设置身份验证标头 (内部用来安全请求其它模块)
     * 配合：配置（openFeign去请求时，自动携带该请求头）
     *
     * @param accessToken 访问令牌
     * @throws Exception 例外
     */
    private static void setAuthenticationHeader(String accessToken) throws Exception {
        // 使用静态方法方式获取request对象
        HeaderMapRequestWrapper requestWrapper = null;
        if (RequestContextHolder.getRequestAttributes() != null) {
            HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
            if (request instanceof HeaderMapRequestWrapper) {
                requestWrapper = (HeaderMapRequestWrapper)request;
            }else {
                requestWrapper = new HeaderMapRequestWrapper(request);
            }
        }else {
            requestWrapper = new HeaderMapRequestWrapper(Mockito.mock(HttpServletRequest.class));
        }
        requestWrapper.addHeader (jwtHelperConfig.getAuthorizationHeader(), "Bearer " + accessToken);
        RequestContextHolder.setRequestAttributes(new ServletRequestAttributes(requestWrapper));
    }

    /**
     * 启用模块安全访问
     * 生成不包含用户信息的token，所以调用enableModuleSecurityAccess发出的请求不能确定用户，只能确认哪个模块
     * tokenData中还有isRequireStateControl=false
     *
     * @throws Exception 例外
     */
    @Deprecated
    public static void useModuleSecurityAccess() {
        try {
            Map<String, Object> tokenData = null;
            try {
                tokenData = jwtHelper.authentication(null, true, false,true);
                if (tokenData != null && tokenData.containsKey(AllTokenDataKeys.MODULE_SECURITY_ACCESS_KEY)) {
                    // 如果请求头已经有了安全的模块访问标记，就不再需要添加了
                    return;
                }
            }catch (Exception e) {
                tokenData = new HashMap<>();
            }
            // 添加安全模块访问标记
            tokenData.put(AllTokenDataKeys.MODULE_SECURITY_ACCESS_KEY, true);
            AuthenticationTokens tokenForModuleRequest = jwtHelper.createAuthenticationTokens(tokenData,false);
            setAuthenticationHeader(tokenForModuleRequest.getAccessToken());
        }catch (Exception e) {
            throw new RuntimeException("无法开启安全模块访问！");
        }
    }


    public static void moduleSecurityAccessTokenProvider(BiConsumer<String, String> keyTokenProvider) {
        // 在这个函数体内，你可能需要做一些你想要用accessTokenProvider实现的东西
        System.err.println("模块安全访问标记");
        try {
            Map<String, Object> tokenData = null;
            try {
                tokenData = jwtHelper.authentication(null, true, false,true);
                System.out.println("访问前存在token");

            }catch (Exception e) {
                tokenData = new HashMap<>();
                System.out.println("访问前无token");
            }
            // 添加安全模块访问标记
            if (!tokenData.containsKey(AllTokenDataKeys.MODULE_SECURITY_ACCESS_KEY)) {
                tokenData.put(AllTokenDataKeys.MODULE_SECURITY_ACCESS_KEY, true);
            }
            AuthenticationTokens authenticationTokens = jwtHelper.createAuthenticationTokens(tokenData, false);
            keyTokenProvider.accept(jwtHelperConfig.getAuthorizationHeader(), authenticationTokens.getAccessToken());
        }catch (Exception e) {
            throw new RuntimeException("无法生成模块安全访问token！");
        }
    }
}
